package admin

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"time"

	"github.com/go-kratos/kratos/v2/errors"
	"gorm.io/gorm"

	wechat "gitee.com/quantum-force/wxopen-api"
	v1 "open/api/open/admin/v1"
	"open/internal/model"
	"open/internal/model/event"
	"open/internal/model/field"
	"open/internal/types"
	"open/pkg/middleware/auth"
	"open/pkg/sync/errgroup"
)

// GetWechatPreAuthCode 获取微信预授权码
func (s *Service) GetWechatPreAuthCode(ctx context.Context, _ *v1.GetWechatPreAuthCodeRequest) (*v1.GetWechatPreAuthCodeReply, error) {
	return s.dao.GetWechatPreAuthCode(ctx)
}

// AuthorizeWechat 微信授权
func (s *Service) AuthorizeWechat(ctx context.Context, req *v1.WechatAuthorizeRequest) (*v1.WechatAuthorizeReply, error) {
	authInfo, err := s.dao.AuthorizeWechat(ctx, req.AuthorizationCode)
	if err != nil {
		return nil, err
	}
	if err := s.dao.Transaction(ctx, func(ctx context.Context) error {
		subApp, err := s.dao.LockWechatAuthApp(ctx, authInfo.AppID)
		if err != nil {
			// 授权通知比前端调用先到
			if errors.Is(err, gorm.ErrRecordNotFound) {
				subApp = &model.WechatOpenSubApp{
					OpenAppID:    authInfo.OpenAppID,
					AppID:        authInfo.AppID,
					Token:        authInfo.Token,
					RefreshToken: authInfo.RefreshToken,
					ExpiredAt:    &authInfo.ExpiredAt,
					Status:       types.WechatAuthorized,
				}
				if err = s.dao.CreateWechatSubApp(ctx, subApp); err != nil {
					return err
				}
			}
		} else {
			subApp.Token = authInfo.Token
			subApp.Status = types.WechatAuthorized
			subApp.RefreshToken = authInfo.RefreshToken
			subApp.ExpiredAt = &authInfo.ExpiredAt
			if err := s.dao.SaveWechatSubApp(ctx, subApp); err != nil {
				return err
			}
		}
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}
	// 查询信息
	authAppInfo, err := s.dao.GetWechatAuthorizerInfo(ctx, authInfo.AppID)
	if err != nil {
		return nil, err
	}
	if authAppInfo.MiniProgramInfo != nil {
		userInfo, err := auth.FromContext(ctx)
		if err != nil {
			return nil, err
		}
		err = s.dao.Transaction(ctx, func(ctx context.Context) error {

			// 品牌和app只能是1对1关系,之前没有控制,一个app可以绑定到多个品牌上了
			brandID := userInfo.BrandID
			appID := authInfo.AppID

			brandMiniApp, err := s.dao.GetBrandMiniAppRefByBrandIDAndPlatform(ctx, brandID, types.Wechat)
			if err != nil {
				if !errors.Is(err, gorm.ErrRecordNotFound) {
					return err
				}

				// 从品牌查没有绑定过,还要再从app查有没有绑定过其他品牌
				_, err = s.dao.GetBrandIDByAppID(ctx, appID, types.Wechat)
				if err != nil {
					if !errors.Is(err, gorm.ErrRecordNotFound) {
						return err
					}
					// 没找到记录,可以继续往下
				} else {
					return errors.InternalServer("BRAND_MINI_APP", "小程序已被其他品牌使用")
				}

			} else {
				// 品牌已经绑定过小程序,需要判断是否是同一个授权的小程序
				if brandMiniApp.AppID != appID {
					return errors.InternalServer("BRAND_MINI_APP", "品牌已经绑定过其他小程序.如果要更换,请先解绑")
				} else {
					return errors.InternalServer("BRAND_MINI_APP", "品牌已经授权当前小程序")
				}
			}

			// // 删除现存跟小程序的关联关系(牵制判断)
			// if err := s.dao.BrandMiniAppRef(ctx, userInfo.BrandID, authInfo.AppID, types.Wechat); err != nil {
			// 	return err
			// }

			// 落库品牌跟小程序关联关系
			if err := s.dao.CreateBrandMiniAppRef(ctx, &model.BrandMiniApp{
				Platform: types.Wechat,
				BrandID:  userInfo.BrandID,
				AppID:    authInfo.AppID,
				EmpID:    userInfo.EmpID,
			}); err != nil {
				return err
			}
			wechatMiniApp := &model.WechatMiniApp{
				AppID:         authInfo.AppID,
				Name:          authAppInfo.Nickname,
				Avatar:        authAppInfo.HeadImg,
				QrCodeURL:     "",
				PrincipalName: authAppInfo.PrincipalName,
				Signature:     authAppInfo.Signature,
			}
			// 落库小程序应用信息
			err = s.dao.CreateWechatMiniApp(ctx, wechatMiniApp)
			if err != nil {
				return err
			}
			// 初始化版本信息
			if err := s.dao.CreateAppVersion(ctx, &model.MiniAppVersion{
				Platform: types.Wechat,
				AppID:    authInfo.AppID,
				Status:   types.StatusAuthorized,
				Stage:    types.StageInitialized,
			}); err != nil {
				return err
			}
			schedule := &model.Schedule{
				RefID:     wechatMiniApp.ID,
				JobType:   types.TaskSyncQRCode,
				JobData:   field.JSON("{}"),
				JobStatus: types.ScheduleTaskInit,
			}
			if err := s.dao.CreateSchedule(ctx, schedule); err != nil {
				return err
			}
			// 发送异步同步微信二维码任务
			return s.dao.TxSend(ctx, &event.TaskSyncQRCode{
				JobID:     schedule.ID,
				AppID:     authInfo.AppID,
				Platform:  types.Wechat,
				Timestamp: time.Now().Unix(),
			})
		})
		if err != nil {
			return nil, err
		}
	}
	return &v1.WechatAuthorizeReply{Message: "ok"}, nil
}

// AddWechatMiniTester 新增微信小程序体验者
func (s *Service) AddWechatMiniTester(ctx context.Context, req *v1.AddWechatMiniTesterRequest) (*v1.AddWechatMiniTesterReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Wechat)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.NotFound("APP_NOT_FOUND", "品牌微信小程序不存在")
		}
		return nil, err
	}
	if err := s.dao.AddWechatMiniTester(ctx, appID, req.WechatId); err != nil {
		if wechatError, ok := err.(*wechat.ErrorResponse); ok {
			switch wechatError.Code {
			case 85001:
				msg := fmt.Sprintf("微信号 %s 不存在或微信号 %s 设置为不可搜索", req.WechatId, req.WechatId)
				err = errors.BadRequest("WECHAT_ERROR", msg)
			case 85002:
				err = errors.BadRequest("WECHAT_ERROR", "小程序绑定的体验者数量达到上限")
			case 85003:
				msg := fmt.Sprintf("微信号 %s 绑定的小程序体验者达到上限", req.WechatId)
				err = errors.BadRequest("WECHAT_ERROR", msg)
			case 85004:
				msg := fmt.Sprintf("微信号 %s 已经绑定", req.WechatId)
				err = errors.BadRequest("WECHAT_ERROR", msg)
			default:
				err = errors.BadRequest("WECHAT_ERROR", wechatError.Message)
			}
		}
		return nil, err
	}
	return &v1.AddWechatMiniTesterReply{Message: "ok"}, nil
}

// DeleteWechatMiniTester 删除微信小程序体验者
func (s *Service) DeleteWechatMiniTester(ctx context.Context, req *v1.DeleteWechatMiniTesterRequest) (*v1.DeleteWechatMiniTesterReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Wechat)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest("APP_NOT_FOUND", "品牌微信小程序不存在")
		}
		return nil, err
	}
	if err := s.dao.DeleteWechatMiniTester(ctx, appID, req.WechatId, req.UserString); err != nil {
		if wechatError, ok := err.(*wechat.ErrorResponse); ok {
			err = errors.BadRequest("WECHAT_ERROR", wechatError.Message)
		}
		return nil, err
	}
	return &v1.DeleteWechatMiniTesterReply{Message: "ok"}, nil
}

// GetWechatMini 获取微信小程序
func (s *Service) GetWechatMini(ctx context.Context, _ *v1.GetWechatMiniRequest) (*v1.GetWechatMiniReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Wechat)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest(v1.ErrorCode_AUTH_NOT_FOUND.String(), "品牌微信小程序不存在")
		}
		return nil, err
	}
	var (
		testers       []*wechat.Tester
		refs          []*model.WechatUserString
		wechatMiniApp *model.WechatMiniApp
		version       *model.MiniAppVersion
	)

	g := errgroup.WithCancel(ctx)
	g.Go(func(ctx context.Context) error {
		wechatMiniApp, err = s.dao.GetWechatMiniApp(ctx, appID)
		if err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				err = errors.BadRequest("APP_NOT_FOUND", "微信小程序不存在")
			}
			return err
		}
		return nil
	})
	g.Go(func(ctx context.Context) error {
		testers, err = s.dao.GetWechatMiniTesters(ctx, appID)
		if err != nil {
			if wechatErr, ok := err.(*wechat.ErrorResponse); ok {
				err = errors.BadRequest("WECHAT_ERROR", wechatErr.Message)
			}
			return err
		}
		return nil
	})
	g.Go(func(ctx context.Context) error {
		version, err = s.dao.GetAppVersion(ctx, appID, types.Wechat)
		if err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				err = errors.BadRequest("APP_VERSION_NOT_FOUND", "微信小程序版本不存在")
			}
			return err
		}
		return nil
	})
	err = g.Wait()
	if err != nil {

		return nil, err
	}
	userStrings := make([]string, len(testers))
	for i, member := range testers {
		userStrings[i] = member.UserString
	}
	refs, err = s.dao.FindWechatIDUserStringRefs(ctx, userStrings)
	if err != nil {
		return nil, err
	}
	ref := make(map[string]string)
	for _, r := range refs {
		ref[r.UserStr] = r.WechatID
	}
	members := make([]*v1.GetWechatMiniReply_Member, len(testers))
	for i, member := range testers {
		members[i] = &v1.GetWechatMiniReply_Member{
			UserString: member.UserString,
			WechatId:   ref[member.UserString],
		}
	}

	return &v1.GetWechatMiniReply{
		Name:          wechatMiniApp.Name,
		Avatar:        wechatMiniApp.Avatar,
		AppId:         wechatMiniApp.AppID,
		PrincipalName: wechatMiniApp.PrincipalName,
		Signature:     wechatMiniApp.Signature,
		QrCodeUrl:     wechatMiniApp.QrCodeURL,

		Version: &v1.GetWechatMiniReply_Version{
			ReleaseVersion: version.ReleaseVersion,
			Status:         version.Status,
			AuditVersion:   version.AuditVersion,
			RejectReason:   version.RejectReason,
			UpdateTime:     version.UpdatedAt.Format("2006-01-02 15:04:05"),
		},
		Members: members,
	}, nil
}

// CommitWechatMini 提交小程序
func (s *Service) CommitWechatMini(ctx context.Context, _ *v1.CommitWechatMiniRequest) (*v1.CommitWechatMiniReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Wechat)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest("APP_NOT_FOUND", "微信小程序不存在")
		}
		return nil, err
	}
	if err = s.dao.Transaction(ctx, func(ctx context.Context) error {
		appVersion, err := s.dao.LockAppVersion(ctx, appID, types.Wechat)
		if err != nil {
			return err
		}
		appVersion.Stage = types.StageInitialized
		appVersion.Status = types.StatusAuditing
		if err = s.dao.SaveAppVersion(ctx, appVersion); err != nil {
			return err
		}
		template, err := s.dao.GetLatestWechatTemplate(ctx)
		if err != nil {
			return errors.InternalServer("INTERNAL", "小程序模版未配置")
		}

		ext := fmt.Sprintf(`{"brand_id": %d, "app_id": "%s"}`, user.BrandID, appID)
		commitTemplate := template.ToVO(fmt.Sprintf(`{"extAppid": "%s", "ext": %s}`, appVersion.AppID, ext))
		jobData, _ := json.Marshal(commitTemplate)
		schedule := &model.Schedule{
			JobType:   types.TaskCommitCode,
			RefID:     appVersion.ID,
			JobData:   field.JSON(jobData),
			JobStatus: types.ScheduleTaskInit,
		}
		if err = s.dao.CreateSchedule(ctx, schedule); err != nil {
			return err
		}
		// 发送事件
		if err = s.dao.TxSend(ctx, event.TaskCommitCode{
			AppID:     appVersion.AppID,
			Platform:  appVersion.Platform,
			Biz:       jobData,
			JobID:     schedule.ID,
			Timestamp: time.Now().Unix(),
		}); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &v1.CommitWechatMiniReply{Message: "ok"}, nil
}

// GetWechatMiniTestQRCode 获取微信小程序二维码
func (s *Service) GetWechatMiniTestQRCode(ctx context.Context, _ *v1.GetWechatMiniTestQRCodeRequest) (*v1.GetWechatMiniTestQRCodeReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Wechat)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest("APP_NOT_FOUND", "微信小程序不存在")
		}
		return nil, err
	}
	resp, err := s.dao.GetWechatMiniTestQRCode(ctx, appID)
	if err != nil {
		if wechatError, ok := err.(*wechat.ErrorResponse); ok {
			return nil, errors.BadRequest("WECHAT_ERROR", wechatError.Message)
		}
		return nil, err
	}
	defer resp.Body.Close()
	buffer := make([]byte, resp.ContentLength)
	_, _ = io.ReadFull(resp.Body, buffer)
	return &v1.GetWechatMiniTestQRCodeReply{
		Content:       buffer,
		ContentLength: resp.ContentLength,
		ContentType:   resp.Header.Get("Content-Type"),
	}, nil
}

// CreateWechatMiniQRCode 创建小程序二维码
func (s *Service) CreateWechatMiniQRCode(ctx context.Context, req *v1.CreateWechatMiniQRCodeRequest) (*v1.CreateWechatMiniQRCodeReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Wechat)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest("APP_NOT_FOUND", "微信小程序不存在")
		}
		return nil, err
	}
	resp, err := s.dao.CreateWechatMiniQRCode(ctx, appID, req)
	if err != nil {
		if wechatError, ok := err.(*wechat.ErrorResponse); ok {
			return nil, errors.BadRequest("WECHAT_ERROR", wechatError.Message)
		}
		return nil, err
	}
	defer resp.Body.Close()
	buffer := make([]byte, resp.ContentLength)
	_, _ = io.ReadFull(resp.Body, buffer)
	return &v1.CreateWechatMiniQRCodeReply{
		Content:       buffer,
		ContentLength: resp.ContentLength,
		ContentType:   resp.Header.Get("Content-Type"),
	}, nil
}
